#include "quirrel_function_extension.hpp"
#include "quirrel_base.hpp"
#include "scripting/scripting_api.h"
#include "scripting/scripting_module.h"
#include "sqrat/sqratTable.h"

#include <cstring>
#include <vector>
#include <algorithm>
#include <iterator>

struct QuirrelFunctionExtensionPrivate{
    std::vector<uint8_t>       byte_code;
};


QuirrelFunctionExtension::QuirrelFunctionExtension(std::string title, std::string script_path):
IFunctionalScript(std::move(title), std::move(script_path), g_current_module),
d{new QuirrelFunctionExtensionPrivate}
{
    
}

QuirrelFunctionExtension::~QuirrelFunctionExtension()
{
    delete d;
}

void QuirrelFunctionExtension::execute()
{
    struct closure_read_data {
        uint8_t* const data;
        SQInteger      length;
        SQInteger      read;
    };

    closure_read_data data{
        .data = d->byte_code.data(),
        .length = static_cast<SQInteger> (d->byte_code.size ()),
        .read =0
    };

    VMHelper &helper = VMHelper::GetVMInstance();

    auto result = sq_readclosure(helper, +[](SQUserPointer p1, SQUserPointer p2, SQInteger p3)->SQInteger {
        const auto data = static_cast<closure_read_data *> (p1);
        memcpy(p2, data->data + data->read, p3);
        data->read += p3;
        return p3;
    }, &data);
    SQ_CHECK_RV(result, "Closure deserialize error", helper);

    //!! run the closure first
    sq_pushroottable(helper);
    result = sq_call(helper, 1, true, true);
    SQ_CHECK_RV(result, "Closure failed to call", helper);
    
    Sqrat::RootTable table{ helper };
    auto main = table.GetFunction("main");
    if (!main.IsNull ())
    {
        main.Execute();
        return;
    }

    SQ_CHECK_RV(SQ_ERROR, "function main not found!!", helper);
}

IScript* QuirrelFunctionExtension::create(std::string title, std::string path, std::string_view code)
{
    QuirrelFunctionExtension *ext = new QuirrelFunctionExtension(std::move(title), std::move(path));
    if (!ext->compile(code)) {
        delete ext;
        ext = nullptr;
    }
    return ext;
}

bool QuirrelFunctionExtension::compile(const std::string_view& code) const
{
    VMHelper &helper = VMHelper::GetVMInstance();

    auto result = sq_compilebuffer (helper, code.data (), code.length (), "main", true);
    SQ_CHECK_R(result, "Code Compile Error", helper, false);
    result = sq_writeclosure (helper, +[](SQUserPointer p1, SQUserPointer p2, SQInteger p3)-> SQInteger {
        const auto data      = static_cast<uint8_t*>(p2);
        const auto byte_code = static_cast<std::vector<unsigned char> *>(p1);
        std::copy_n(data, p3, std::back_inserter (*byte_code));
        return p3;
    }, &d->byte_code);
    SQ_CHECK_R(result, "Closure serialize error", helper, false);
    return true;
}
